#pragma once
#include <memory>
#include "Socket.hpp"
#include "Log.hpp"
#include <sys/select.h>

//select 是最早提出的解决方案存在着许多的问题！！！！


const static int gdefault_port = 8888;
const static int gdefault_backlog = 16;
const static int array_num = sizeof(fd_set) * 8;

class SelectServer
{
public:
    SelectServer(int16_t port = gdefault_port)
        : _port(port), _listensock(std::make_unique<TcpSocket>()), _isruning(false)
    {
    }
    void Init()
    {
        _listensock->BuildListenSocketMethod(_port, gdefault_backlog);
        for (int i = 0; i < array_num; i++)
        {
            _array_sock[i] = nullptr;
        }
        _array_sock[0] = _listensock.get();
    }
    void Loop()
    {
        _isruning = true;
        while (_isruning)
        {
            // 直接获取链接吗？？？？Accept获取连接本质也是等 + 获取，所有我们将他托管到select上来等
            fd_set rfds; // 今天我们的server只关心读事件
            FD_ZERO(&rfds);
            // 根据_array_sock来重置rfds
            int max_fd = -1;
            for (int i = 0; i < array_num; i++)
            {
                if (_array_sock[i] != nullptr)
                {
                    FD_SET(_array_sock[i]->GetSockFd(), &rfds);
                    max_fd = std::max(max_fd, _array_sock[i]->GetSockFd());
                }
            }
            // struct timeval timeout = {4, 0}; // 阻塞 + 非阻塞
            struct timeval timeout = {0, 0};//非阻塞
            //nullptr 阻塞
            PrintDebug();
            int r = select(max_fd + 1, &rfds, nullptr, nullptr, &timeout); // 后面得四个参数都是输入输出型参数
            if (r > 0)
            {
                lg(Info, "select success, begin event handler, last time: %u.%u", timeout.tv_sec, timeout.tv_usec);
                EventDistribution(rfds);
            }
            else if (r == 0)
            {
                lg(Info, "select timeout..., last time %u:%u", timeout.tv_sec, timeout.tv_usec);
            }
            else
            {
                lg(Error, "select error !!!!");
                Stop();
            }
            sleep(1);
        }
        _isruning = false;
    }
    void Stop()
    {
        _isruning = false;
    }
    ~SelectServer()
    {
    }
    void PrintDebug()
    {
        std::cout << "rfds : ";
        for (int i = 0; i < array_num; i++)
        {
            if (_array_sock[i] != nullptr)
            {
                std::cout << _array_sock[i]->GetSockFd() << "  ";
            }
        }
        std::cout << "\n";
    }

private:
    void EventDistribution(fd_set &rfds)
    {
        for (int i = 0; i < array_num; i++)
        {
            if (_array_sock[i] == nullptr)
                continue;
            int fd = _array_sock[i]->GetSockFd();
            if (FD_ISSET(fd, &rfds)) // 判断fd是否就绪
            {
                // 判断是普通读事件还是获取连接
                if (fd == _listensock->GetSockFd())
                {
                    std::string clientip;
                    uint16_t port;
                    Socket *sock = _listensock->AcceptSocketOrDie(&clientip, &port); // 这里一定是不需要等待的，只需要直接获取连接
                    if (!sock)
                        return;
                    lg(Info, "get a new link, client info : %s:%d   fd : %d", clientip.c_str(), port, sock->GetSockFd());
                    // fd添加到数组中托管
                    int pos = 0;
                    for (; pos < array_num; pos++)
                    {
                        if (_array_sock[pos] == nullptr)
                        {
                            _array_sock[pos] = sock;
                            break;
                        }
                    }
                    // 说明已经达到select管理文件描述符的上线
                    if (pos == array_num)
                    {
                        sock->SockFdClose(); // 直接关闭
                        delete sock;
                        lg(Info, "select full!!!");
                    }
                }
                else // 普通fd
                {
                    // 普通的读事件就绪
                    // 读数据是有问题的
                    // 这一次读取不会被卡住吗？ 协议问题
                    std::string out;
                    bool ret = _array_sock[i]->Recv(&out, 1024);
                    if (ret)
                    {
                        std::cout << "client say# " << out << std::endl;
                        out += "你好呀 selectserver!!!";
                        _array_sock[i]->Send(out);
                        out.clear();
                    }
                    else
                    {
                        lg(Warning, "client quit, maybe close or error, close fd : %d", fd);
                        _array_sock[i]->SockFdClose();
                        delete _array_sock[i];
                        _array_sock[i] = nullptr;
                    }
                }
            }
        }
    }
private:
    std::unique_ptr<Socket> _listensock;
    int16_t _port;
    bool _isruning;
    Socket *_array_sock[array_num]; 
};